﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MentalAlchemy.Atomics;

namespace MentalAlchemy.Molecules.MachineLearning.GradEAAlg
{
	/// <summary>
	/// [molecule]
	/// 
	/// Class to store information about changes made for some objects.
	/// </summary>
	[Serializable]
	public class ChangeData : ICloneable
	{
		#region - Properties. -
		/// <summary>
		/// Indicates which operator made the changes.
		/// </summary>
		public Operator Actor { get; set; }

		/// <summary>
		/// Changed individual (initial state).
		/// </summary>
		public Individual Individual { get; set; }

		/// <summary>
		/// Parameters to change the [Individual].
		/// 
		/// todo: think of making separate object to hold the changes. It'll be very abstract though...
		/// </summary>
		public float[] Change { get; set; }

		/// <summary>
		/// Change of fitness due to [Change].
		/// </summary>
		public Fitness FitnessChange { get; set; }

		/// <summary>
		/// Estimate for gradient vector length and direction.
		/// - positive (negative) value correponds to growth (reduction) of fitness values (not to the growth/reduction of quality!!!)
		/// - modulus defines how strong the change is.
		/// </summary>
		public float[] GradientEstimate { get; set; } 
		#endregion

		#region - Construction. -
		public ChangeData () {}
		public ChangeData(ChangeData data)
		{
			Assign(data);
		}
		#endregion

		public object Clone ()
		{
			return new ChangeData(this);
		}

		/// <summary>
		/// [molecule]
		/// 
		/// Assigns current ChangeData to the prescribed one.
		/// </summary>
		/// <param name="data"></param>
		public void Assign(ChangeData data)
		{
			Actor = data.Actor;
			Individual = data.Individual;
			Change = (float[])data.Change.Clone();
			FitnessChange = data.FitnessChange.Clone();
			GradientEstimate = (float[])data.GradientEstimate.Clone();
		}

		/// <summary>
		/// [molecule]
		/// 
		/// Compares two different data changes.
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public bool Equals (ChangeData data)
		{
			if (Actor != data.Actor) { return false; }
			if (!VectorMath.Equal(Change, data.Change, float.Epsilon)) { return false; }
			if (!FitnessChange.Equals(data.FitnessChange)) { return false; }
			if (!Individual.Equals(data.Individual)) { return false; }
			//if (!VectorMath.Equal(GradientEstimate, data.GradientEstimate, float.Epsilon)) { return false; }
			return true;
		}

		/// <summary>
		/// [molecule]
		/// 
		/// Changes sign for the [Change] vector components.
		/// </summary>
		public void ReverseChange ()
		{
			Change = VectorMath.Mul(Change, -1f);
		}

		public override string ToString ()
		{
			var res = "";
			res += String.Format("operator:\t{0}\tindividual:\t{1}\tchange:\t{2}\tfit. change:\t{3}\tgrad:\t{4}",
			                     Actor.Name,
			                     VectorMath.ConvertToString(Individual.Genes.ToArray(), '\t'),
			                     VectorMath.ConvertToString(Change, '\t'),
								 FitnessChange.Value,
								 VectorMath.ConvertToString(GradientEstimate, '\t')
								 );
			return res;
		}
	}
}